package org.biodas.jdas.converter;

import org.apache.log4j.Logger;
import org.biodas.jdas.client.FeaturesClient;
import org.biodas.jdas.client.adapters.features.DasGFFAdapter;
import org.biodas.jdas.schema.features.*;

import java.math.BigInteger;
import java.util.HashMap;
import java.util.List;

/**
 * Converts 1.5 spec features so that they have parent parts and not groups.
 * LPGL
 *@author JWarren
 *
 */
public class SpecFeaturesConverter {
    private static Logger logger = Logger.getLogger(SpecFeaturesConverter.class);
    FeaturesClient fClient;


    public DasGFFAdapter convert15To16(DasGFFAdapter results) {
        return this.convert15To16(results, true);
    }

    /**
     * convert a default schema that contains 1.5 and 1.6 capabilities ie parts
     * and groups to a 1.6 with parents and parts - groups can then be ignored.
     *
     * @param results
     * @param  removeGroups - true means the output xml will not contain groups so recommended
     * @return
     */
    public DasGFFAdapter convert15To16(DasGFFAdapter results, boolean removeGroups) {
        List<SEGMENT> segmentsAdapters = this.getSegments(results);// we are
        // only
        // interested
        // in
        // converting
        // real
        // segments
        // here
        // not
        // errors.
        for (SEGMENT segmentAdapter : segmentsAdapters) {
            List<FEATURE> features = segmentAdapter.getFEATURE();
            HashMap<String, GroupRange> groupMap = new HashMap();
            for (FEATURE feature : features) {
                // loop over all the features and find the ones with a group id.
                if (feature.getGROUP() != null) {
                    for (GROUP group : feature.getGROUP()) {
                        PARENT parent = new PARENT();// parent reference for
                        // this child feature -
                        // all it needs is an id
                        // to reference a new
                        // parent feature which
                        // we will add at the
                        // end to represent the
                        // group
                        parent.setId(group.getId());
                        feature.getPARENT().add(parent);
                        setGroupRange(groupMap, group, feature.getSTART(),
                                feature.getEND(), feature.getId());
                    }
                }
                // straight away we can assign a parent tag to this feature with
                // the group_id as the parent
                // store the start and stop of the feature with the groupid if
                // the start is smaller or stop is larger than whats already
                // been seen for that groupid.

                logger.debug(feature.getGROUP().get(0).getId());
                //remove groups if specified (saves memory?) but can leave them in for debugging purposes
                if(removeGroups)feature.getGROUP().clear();
            }

            //now we have looped over all the features gotten the groups we can create the feature to represent the "parent" group and add these to the features list
            for(String groupId: groupMap.keySet()){
                GroupRange finalGroupRange=groupMap.get(groupId);
                FEATURE gFeature=new FEATURE();
                gFeature.setId(groupId);
                gFeature.setSTART(finalGroupRange.getStart());
                gFeature.setEND(finalGroupRange.getEnd());
                GROUP group=finalGroupRange.getGroup();
				//add notes, links and targets from the group to the new parent feature
                for(Object obj:group.getNOTEOrLINKOrTARGET()){
					if(obj instanceof LINK){
						gFeature.getLINK().add((LINK)obj);
					}
					if(obj instanceof String){
						gFeature.getNOTE().add((String)obj);
					}
					if(obj instanceof TARGET){
						gFeature.getTARGET().add((TARGET)obj);
					}
				}
				//loop over all the feature ids that belong to this group and add them as parts
				for(String partId:finalGroupRange.getParts()){
					PART part=new PART();
					part.setId(partId);
					gFeature.getPART().add(part);
				}
				segmentAdapter.getFEATURE().add(gFeature);
				
			}

		}
		return results;
	}

    /**
     * if start or stop of this feature is outside the range currently stored
	 * for that group change it
     * @param groupMap
     * @param group
     * @param featureStart
     * @param featureEnd
     * @param featureId
     */
	private void setGroupRange(HashMap<String, GroupRange> groupMap, GROUP group,
			BigInteger featureStart, BigInteger featureEnd, String featureId) {
		if (group.getId() != null) {
			String groupId = group.getId();
			if (groupMap.containsKey(groupId)) {
				// get the current range for this group and see if this extends
				// it
				GroupRange groupRange = groupMap.get(groupId);
				logger.debug("groupid already encountered with start="
						+ groupRange.getStart() + " end=" + groupRange.getEnd());

				if (featureStart.compareTo(groupRange.getStart()) == -1) {
					groupRange.setStart(featureStart);
				}
				if (featureEnd.compareTo(groupRange.getEnd()) == 1) {
					groupRange.setEnd(featureEnd);
				}
				groupRange.addPart(featureId);

				// if(new BigIntfeature.getSTART()<groupRange.getStart())
			} else {
				groupMap.put(groupId, new GroupRange(featureStart, featureEnd, group, featureId));
				logger.debug("adding new range groupid=" + groupId + " start="
						+ featureStart + " end=" + featureEnd);
			}
		}
	}

	private List<SEGMENT> getSegments(DasGFFAdapter dasGffAdapter) {
		List<SEGMENT> segmentsAdapters = dasGffAdapter.getGFF().getSegments();
		return segmentsAdapters;
	}

}
